import sys
import mimetypes
from fusion.db.profile import Profile
from fusion.db.invitation import decode_invitation
from fusion.db.util import get_timestamp
from mako.lookup import TemplateLookup
from binascii import hexlify, unhexlify

class BaseController(object):
    def __init__(self):
        self.lookup = TemplateLookup(directories=['fusion/http/templates', 'templates'], filesystem_checks=True, input_encoding='utf-8', output_encoding='utf-8', encoding_errors='replace')

        self._transient_invitations = {}
        self._invitations_awake = False

    def load_view(self, view_name):
        return self.lookup.get_template(view_name)
                    
    def _get_mime_type(self, filename):
        type, encoding = mimetypes.guess_type(filename)
        #[FIXME: ignore encoding, even though we shouldn't really]
        return type or 'application/octet-stream'
                    

    #[TODO: need some escaping?]
    def _process_friend_list(self, db, iid, my_iid=''):
        v = {'items':{}, 'unverified':0, 'index':[]}

        friend_list, unverified = db.report_friends(iid)
        v['unverified'] = unverified

        my_friend_list = []
        if not iid == my_iid:
            my_friend_list, my_unverified = db.report_friends(my_iid)

        for fiid in friend_list:
            v['index'].append(fiid)

            friend_profile = Profile()
            friend_profile.read(db, fiid)

            v['items'][fiid] = {}
            v['items'][fiid]['iid'] = fiid
            v['items'][fiid]['name'] = friend_profile.name or fiid
            v['items'][fiid]['picture'] = '/static/dummy.png'
            if friend_profile.pic:
                v['items'][fiid]['picture'] = 'http://%s.hexlabel.net/content%s' % (fiid, friend_profile.pic_path)
            
            if fiid == my_iid:
                v['items'][fiid]['is_self'] = True
            else:
                v['items'][fiid]['is_self'] = False
            
            if fiid in my_friend_list:
                v['items'][fiid]['is_mutual'] = True
            else:
                v['items'][fiid]['is_mutual'] = False

        # sort by name
        v['index'].sort(cmp=lambda x, y: cmp(v['items'][x]['name'].lower(), v['items'][y]['name'].lower()))

        return v


    #[TODO: need some escaping?]
    def _process_group_list(self, db, iid, my_iid=''):
        if not my_iid:
            my_iid = iid

        v = {'items':{}, 'index': []}

        group_list = db.list_memberships(iid)
        my_group_list = []
        if not iid == my_iid:
            my_group_list = db.list_memberships(my_iid)

        for giid in group_list:
            v['index'].append(giid)

            gprofile = Profile()
            gprofile.read(db, giid)

            v['items'][giid] = {}
            v['items'][giid]['iid'] = giid
            v['items'][giid]['name'] = gprofile.name
            if not gprofile.name:
                #[FIXME: this is redundant]
                v['items'][giid]['name'] = db.get(giid, 'info/name.txt')
            if not v['items'][giid]['name']:
                v['items'][giid]['name'] = 'Unnamed Group'

            v['items'][giid]['picture'] = '/static/dummy_group.png'
            if gprofile.pic:
                v['items'][giid]['picture'] = 'http://%s.hexlabel.net/content%s' % (giid, gprofile.pic_path)

            if giid in my_group_list:
                v['items'][giid]['is_mutual'] = True
            else:
                v['items'][giid]['is_mutual'] = False

            my_roles = db.get_roles(giid, my_iid)
            if 'admin' in my_roles:
                v['items'][giid]['is_admin'] = True
            else:
                v['items'][giid]['is_admin'] = False

        # sort by name
        v['index'].sort(cmp=lambda x, y: cmp(v['items'][x]['name'].lower(), v['items'][y]['name'].lower()))

        return v


    def _process_member_list(self, db, iid, my_iid):
        v = {'items':{}, 'index':[]}

        members = db.list_members(iid)

        my_friend_list, my_unverified = db.report_friends(my_iid)

        for miid in members:
            v['index'].append(miid)

            name = 'Somebody'
            picture = '/static/dummy.png'

            mprofile = Profile()
            mprofile.read(db, miid)
            if mprofile:
                name = mprofile.name
                if mprofile.pic:
                    picture = mprofile.pic_path

            roles = db.get_roles(iid, miid)

            v['items'][miid] = {}
            v['items'][miid]['iid'] = miid
            v['items'][miid]['name'] = name
            v['items'][miid]['picture'] = 'http://%s.hexlabel.net/content%s' % (miid, picture)
            v['items'][miid]['roles'] = roles

            if miid == my_iid:
                v['items'][miid]['is_self'] = True
            else:
                v['items'][miid]['is_self'] = False

            if miid in my_friend_list:
                v['items'][miid]['is_friend'] = True
            else:
                v['items'][miid]['is_friend'] = False

        # sort by name
        v['index'].sort(cmp=lambda x, y: cmp(v['items'][x]['name'].lower(), v['items'][y]['name'].lower()))

        return v


    def _process_person_profile(self, db, profile):
        # process fields for display
        v = {}
        v['generic'] = {}

        #-- name (always public)
        v['generic']['name'] = {'meta':{}, 'values':[{}]}
        v['generic']['name']['meta']['label'] = 'Name'
        v['generic']['name']['values'][0]['id'] = 'generic#0#name'
        v['generic']['name']['values'][0]['value'] = profile.name
        v['generic']['name']['values'][0]['error'] = None
        v['generic']['name']['values'][0]['visibility'] = True
        
        #-- profile picture (always public)
        v['generic']['picture'] = {'meta':{}, 'values':[{}]}
        v['generic']['picture']['meta']['label'] = 'Picture'
        v['generic']['picture']['values'][0]['id'] = 'generic#0#picture'
        v['generic']['picture']['values'][0]['value'] = '/static/dummy.png'
        v['generic']['picture']['values'][0]['error'] = None
        v['generic']['picture']['values'][0]['visibility'] = True
        if profile.pic:
            v['generic']['picture']['values'][0]['value'] = '/content%s' % profile.pic_path
        
        #-- about me blurb
        v['generic']['about'] = {'meta':{}, 'values':[{}]}
        v['generic']['about']['meta']['label'] = 'About'
        v['generic']['about']['values'][0]['id'] = 'generic#0#about'
        v['generic']['about']['values'][0]['error'] = None
        if profile.public.about:
            v['generic']['about']['values'][0]['value'] = profile.public.about
            v['generic']['about']['values'][0]['visibility'] = True
        elif profile.restricted.about:
            v['generic']['about']['values'][0]['value'] = profile.restricted.about
            v['generic']['about']['values'][0]['visibility'] = False
        else:
            v['generic']['about']['values'][0]['value'] = ''
            v['generic']['about']['values'][0]['visibility'] = False
        
        #-- other
        for section in profile.list_section_names():
            v[section] = {}
            v[section]['meta'] = {}
            num_vis_sec = 0
            for field in profile.list_field_names(section):
                v[section][field] = {'meta':{}, 'values':[]}
                v[section][field]['meta']['label'] = field.capitalize()

                i = 0
                num_vis_fld = 0
                for value, visibility in profile.list_field_values_and_visibility(section, field):
                    v[section][field]['values'].append({})
                    v[section][field]['values'][i]['id'] = '%s#%s#%s' % (section, i, field)
                    v[section][field]['values'][i]['value'] = value
                    v[section][field]['values'][i]['visibility'] = visibility
                    if visibility:
                        num_vis_fld += 1

                    v[section][field]['values'][i]['error'] = None
                    i += 1

                v[section][field]['meta']['num_visible'] = num_vis_fld
                if num_vis_fld > 0:
                    num_vis_sec += 1

            v[section]['meta']['num_visible'] = num_vis_sec

        # fill in blanks for the basic fields
        basic = (('personal', ('born', 'gender')), ('location', ('country', 'region', 'city')), ('contact', ('email', 'website', 'phone', 'im')))
        for basic_section, basic_fields in basic:
            if not v.get(basic_section):
                v[basic_section] = {}                        
                v[basic_section]['meta'] = {}                        
                v[basic_section]['meta']['num_visible'] = 0

            for basic_field in basic_fields:
                if not v[basic_section].get(basic_field):
                    v[basic_section][basic_field] = {'meta':{}, 'values':[{}]}
                    v[basic_section][basic_field]['meta']['label'] = basic_field.capitalize()
                    v[basic_section][basic_field]['meta']['num_visible'] = 0
                    v[basic_section][basic_field]['values'][0]['id'] = '%s#0#%s' % (basic_section, basic_field)
                    v[basic_section][basic_field]['values'][0]['value'] = ''
                    v[basic_section][basic_field]['values'][0]['visibility'] = False
                    v[basic_section][basic_field]['values'][0]['error'] = None
        
        # adjust some labels
        v['contact']['im']['meta']['label'] = 'Instant Messenger'

        return v



    def _process_group_profile(self, db, profile, iid=''):
        # process fields for display
        v = {}
        v['generic'] = {}

        #-- name (always public)
        v['generic']['name'] = {'meta':{}, 'values':[{}]}
        v['generic']['name']['meta']['label'] = 'Name'
        v['generic']['name']['values'][0]['id'] = 'generic#0#name'
        v['generic']['name']['values'][0]['value'] = profile.name or ''
        v['generic']['name']['values'][0]['error'] = None
        v['generic']['name']['values'][0]['visibility'] = True

        if not profile.name:
            #[FIXME: this is redundant]
            v['generic']['name']['values'][0]['value'] = db.get(iid, 'info/name.txt')
        if not v['generic']['name']['values'][0]['value']:
            v['generic']['name']['values'][0]['value'] = 'Unnamed Group'
            
    
        #-- profile picture (always public)
        v['generic']['picture'] = {'meta':{}, 'values':[{}]}
        v['generic']['picture']['meta']['label'] = 'Picture'
        v['generic']['picture']['values'][0]['id'] = 'generic#0#picture'
        v['generic']['picture']['values'][0]['value'] = '/static/dummy_group.png'
        v['generic']['picture']['values'][0]['error'] = None
        v['generic']['picture']['values'][0]['visibility'] = True
        if profile.pic:
            v['generic']['picture']['values'][0]['value'] = '/content%s' % profile.pic_path
        
        #-- about me blurb
        v['generic']['about'] = {'meta':{}, 'values':[{}]}
        v['generic']['about']['meta']['label'] = 'About'
        v['generic']['about']['values'][0]['id'] = 'generic#0#about'
        v['generic']['about']['values'][0]['error'] = None
        if profile.public.about:
            v['generic']['about']['values'][0]['value'] = profile.public.about
            v['generic']['about']['values'][0]['visibility'] = True
        elif profile.restricted.about:
            v['generic']['about']['values'][0]['value'] = profile.restricted.about
            v['generic']['about']['values'][0]['visibility'] = False
        else:
            v['generic']['about']['values'][0]['value'] = ''
            v['generic']['about']['values'][0]['visibility'] = False

        return v


    def _process_dataset(self, db, iid):
        v = {}
        v['info'] = db.get_info(iid)
        v['keys'] = db.keys(iid)

        # filter out system keys
        v['keys'] = [k for k in v['keys'] if not k.startswith('admin/')and not k.startswith('member/') and not k.startswith('rights/')]

        v['data'] = {}
        for k in v['keys']:
            if k.endswith('.json'):
                v['data'][k] = db.get_json(iid, k)
            elif k.endswith('.txt'):
                v['data'][k] = db.get_text(iid, k)
            elif k.endswith('.jpg') or k.endswith('.png') or k.endswith('.gif'):
                v['data'][k] = '<img src="http://%s.hexlabel.net/content/%s"/>' % (iid, k)
            elif k.endswith('.torrent'):
                v['data'][k] = 'BINARY'
            else:
                v['data'][k] = db.get(iid, k)
        #print "*********************^^^^^^^^^^^^^^^^^^^[%s]" % v

        return v


    def _process_all_datasets(self, db):
        v = {}
        v['principal'] = {}
        v['group'] = {}
        v['content'] = {}

        # principals
        ds_p = set(db.list_persons())
        for iid in ds_p:
            v['principal'][iid] = {}
            
            # info
            v['principal'][iid]['info'] = db.get_info(iid)

            # get name if available
            v['principal'][iid]['name'] = db.get_text(iid, '/public/name.txt') or '--Unknown--'

        # groups
        ds_g = set(db.list_groups())
        for iid in ds_g:
            v['group'][iid] = {}
            
            # info
            v['group'][iid]['info'] = db.get_info(iid)

            # get name if available
            v['group'][iid]['name'] = db.get_text(iid, '/public/name.txt') or '--Unknown--'

        # content
        ds_c = set(db.list_subscriptions()) - ds_g - ds_p
        for iid in ds_c:
            v['content'][iid] = {}
            
            # info
            v['content'][iid]['info'] = db.get_info(iid)

            # get name if available
            v['content'][iid]['name'] = db.get_text(iid, '/public/name.txt') or iid

        return v


    # INVITATIONS
    # ---------------
    def _get_invitations(self, db):
        if not self._invitations_awake:
            self._wake_invitations_rep(db)

        return self._transient_invitations


    def _add_invitation(self, db, invitation, code):
        # create and persist invitation
        rep = self._get_invitations_rep(db)
        encoded_i = hexlify(invitation.encode())
        rep[invitation.iid] = {'iid': invitation.iid, 'code': code, 'invitation':encoded_i, 'ts':get_timestamp()}

        self._put_invitations_rep(db, rep)

        # hold reference as long as the deamon is alive
        name = 'Somebody'
        picture = '/static/dummy.png'
        iprofile = Profile()
        iprofile.read(db, invitation.iid)
        if iprofile:
            name = iprofile.name
            if iprofile.pic:
                picture = iprofile.pic_path

        self._transient_invitations[invitation.iid] = {'iid': invitation.iid, 'code': code, 'invitation':invitation, 'ts':get_timestamp(), 'name': name, 'picture': 'http://%s.hexlabel.net/content%s' % (invitation.iid, picture)}


    def _delete_invitation(self, db, iid):
        rep = self._get_invitations_rep(db)
        if rep.has_key(iid):
            del rep[iid]
            self._put_invitations_rep(db, rep)
        
        if self._transient_invitations.has_key(iid):
            del self._transient_invitations[iid]


    def _wake_invitations_rep(self, db):
        #self._put_invitations_rep(db, {})
        rep = self._get_invitations_rep(db)

        for k, item in rep.items():
            i_iid = item['iid']
            if not self._transient_invitations.has_key(i_iid):
                if True:
                    real_invitation = decode_invitation(unhexlify(item['invitation']))
                    if real_invitation:
                        type = db.get_type(real_invitation.iid)
                        if type == 'group':
                            name = 'Some Group'
                            picture = '/static/dummy_group.png'
                        else:
                            name = 'Somebody'
                            picture = '/static/dummy.png'

                        iprofile = Profile()
                        iprofile.read(db, real_invitation.iid)
                        if iprofile:
                            if iprofile.name:
                                name = iprofile.name

                            if iprofile.pic:
                                picture = iprofile.pic_path

                        self._transient_invitations[i_iid] = {'iid': i_iid, 'code': item['code'], 'invitation':real_invitation, 'ts':item['ts'], 'name': name, 'picture': 'http://%s.hexlabel.net/content%s' % (real_invitation.iid, picture)}
                else:
                    # [FIXME: ignore?]
                    pass


    def _get_invitations_rep(self, db):
        iid = db.get_my_iid()
        cipher = db.get_domain_cipher(iid)       

        invitations = db.get_json(iid, '/restricted/invitations.json', cipher=cipher)
        if not invitations:
            return {}

        return invitations


    def _put_invitations_rep(self, db, invitations):
        iid = db.get_my_iid()
        cipher = db.get_domain_cipher(iid)       
        
        db.put(iid, '/restricted/invitations.json', invitations, cipher=cipher)


